Communicating through context

Posted on 2023-02-10 by

henrikvilhelmberglund

getContext and setContext are run at component initialization . Even if we change the context later it won't update.

Here we have a component with a color picker. We want to update the colors of the children using that color picker. How do we do that?
red
Child red
Child red
Count: 0
<script>
	import Parent from "./Parent.svelte";
	import { setContext } from "svelte";

	let color = "red";
	let count = 0;

	setContext("color", color);

	function onClick() {
		count++;
	}
</script>

<input bind:value={color} type="color" />

{color}

<Parent on:click={onClick} />

Count: {count}

We could have the color variable be an object instead.

Here we're sending the whole object using context.
red
Child red
Child red
Count: 0
<script>
	import Parent2 from "./Parent2.svelte";
	import { setContext } from "svelte";

	let count = 0;
	const colorListeners = new Set();
	let colorObj = {
		color: "red",
		listenToColorChange(fn) {
			colorListeners.add(fn);
		},
	};
	$: colorListeners.forEach((listener) => listener(colorObj.color));

	setContext("color", colorObj);

	function onClick() {
		count++;
	}
</script>

<input bind:value={colorObj.color} type="color" />

{colorObj.color}

<Parent2 on:click={onClick} />

Count: {count}

If we want to go from child to parent we can use callbacks or events instead.

In the above example we're doing this by forwarding dispatched events (on:click) that run a function in the parent.

Let's try doing it using context instead:

Any children within this component can read the onClick function from the click context.
red
Child red
Child red
Count: 0
<script>
	import Parent3 from "./Parent3.svelte";
	import { setContext } from "svelte";

	let count = 0;
	const colorListeners = new Set();
	let colorObj = {
		color: "red",
		listenToColorChange(fn) {
			colorListeners.add(fn);
		},
	};
	$: colorListeners.forEach((listener) => listener(colorObj.color));

	setContext("color", colorObj);
	setContext("click", onClick);

	function onClick(delta) {
		count += delta;
	}
</script>

<input bind:value={colorObj.color} type="color" />

{colorObj.color}

<Parent3 />

Count: {count}

By doing that we could remove the dispatched events and events from the parents and just run the function inside the child using getContext.

We can also pass different data up by changing the function inputs.

Here the child is a publisher of events and the app is a subscriber of the events .